// INTERPRETER.CPP : Container Class for code + interpeter
//
//LICENSED FREELY UNDER MIT.
//CREATED FOR FREE USE.

#include "stdafx.h"
#include "Interpreter.h"
#include "lexer.h"
#include "variabledomain.h"
#include "Parser.h"


//Constructor to create instance of interpreter.
Interpreter::Interpreter(char * name,void ** ParentDomain ){
	strcpy_s( this->name, sizeof(this->name), name);
	this->State = READY_NOINPUT;
	this->ErrorHandlingMode = PROCESS_ERROR_DIE; //autoit-like error out when problem occurs.
	this->Domain = new VariableDomain( this, (VariableDomain*)ParentDomain);

	//Loadup the builtin-function pointertable with the correct addresses
	InitBuiltinTable_Defaults( &this->BuiltinTable);
}


//Function is used to get the interpreter to start executing the code.
int Interpreter::Start(void){
	int iPos = 0;
	return 	ExecuteCode(&this->tokenlist, this, &iPos, (VariableDomain*)this->Domain);
}



//This function is used to tell the interpreter the file location of the code
//to execute.
int Interpreter::Set_Raw_CodeFILE(char* filename){

	//We get the lexer to open the file and create tokens.
	Lexer lexer(filename);
	//if (argc > 1){lexer.Lex_File(argv[1]);}
	lexer.Lex_File(filename);

	//The tokens are being stored in the lexer. Grab them and load them in a vector type.
	std::list<token>* Tokens = lexer.Export_Tokens();
	std::list<token>::iterator i;
	for( i = Tokens->begin(); i != Tokens->end(); ++i){
		this->tokenlist.push_back( *i);
	}

	lexer.Dump_Tokens( "C:\\tokens.dump");

	//When a func returns 0, it has failed.
	if( !this->LoadParse_IndexFuncs())return 0;
	return 1;
}


//Iterates through the tokens, and locate and index user functions.
int Interpreter::LoadParse_IndexFuncs(void){
	unsigned int tokiterator = 0;

	//We iterate through the tokens, first locating the ones declaring a function.
	for( ;tokiterator < this->tokenlist.size() ; tokiterator++){
		if(this->tokenlist[tokiterator].type==FUNC_DEC_TOK){
			//We have found a function, save its name.
			Functionentry* temp = new Functionentry;
			temp->name = this->tokenlist[tokiterator].data;
			temp->num_params = 0;
			temp->num_opt_params = 0;
			int ParameterHasOption = 0;


			//Locate and save params. Remember func calls are not permitted in param expressions.
			tokiterator+=2; //Get past func name and '('
			while(this->tokenlist[tokiterator].type!=EOL_TOK){
				if(this->tokenlist[tokiterator].type==PARAM_SEPARATOR_TOK){ //End of parameter?
					temp->num_params++;
					tokiterator++;
					ParameterHasOption = 0;
				}

				if(this->tokenlist[tokiterator].type==EQUAL_TOK){ //Is this parameter optional?
					if(ParameterHasOption){
						return this->Error(INIT_ERROR_PARAMILLEGAL, "Illegal '=' in function parameter.");
					}else{
						ParameterHasOption = 1;
						temp->num_opt_params++;
					}
				}
				token* tempe = new token;
				tempe->data = this->tokenlist[tokiterator].data;
				tempe->type = this->tokenlist[tokiterator].type;
				temp->parameters[temp->num_params].push_back(*tempe);
				tokiterator++;
			}

			//One of the tokens inserted on the last parameter was the closing ')' of the func. Remove.
			temp->parameters[temp->num_params].pop_back();
			//If there was not 0 parameters, remember to increment num_params to make the first param show up.
			if(temp->parameters[temp->num_params].size()>0)temp->num_params++;


			this->Functions.push_back(*temp);


			//int r = 0;			//D E B U G :D
			//for (;r<this->Functions[this->Functions.size()-1].num_params;r++){
			//	std::cout << "\n";
			//	int x=0;
			//	for(;x<this->Functions[this->Functions.size()-1].parameters[r].size();x++){
			//		std::cout << this->Functions[this->Functions.size()-1].parameters[r][x].data<<" ";
			//	}
			//}
		}
		tokiterator++;
	}
	return 1;
}


//Debug function to drop a list of variables to stdout.
void Interpreter::Dump_Variables(void){
	VariableDomain* d = (VariableDomain*)this->Domain;
	d->COUTdump();
}


//Returns 0 if it wants the interpreter to halt.
int Interpreter::Error(int errorcode, char* message){

	if(this->ErrorHandlingMode == INTERPRETER_HALT){
		this->State = GENERAL_FAULT;
		if(errorcode == INIT_ERROR_PARAMILLEGAL)this->State = INIT_FAULT;
		return 0;
	}
	else if(this->ErrorHandlingMode == PROCESS_ERROR_DIE){ //Error and kill process.
	
		char msg[2048] = "Error in code execution: ";
		char errorbuf[10];
		itoa( errorcode, (char*)&errorbuf, 10);
		strcat((char*)&msg, errorbuf);
		strcat((char*)&msg, "\n\n");
		strcat((char*)&msg, message);
		MessageBoxA(NULL, msg, ERRORMSG_TITLE , 0x10) ;
		ExitProcess(-1);
	
	}else{ //This should Never Fire.
		errorcode = INTERPRETER_ERROR;
		MessageBoxA(NULL,"Internal Interpreter Error:\n\nInvalid Error Handling State!" , ERRORMSG_TITLE , MB_OK) ;
		ExitProcess(-1);
	}
}